/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id$
 */
package wx.xml.xalan.xalan.templates;

import java.util.Hashtable;

import wx.xml.xalan.xalan.transformer.KeyManager;
import wx.xml.xalan.xalan.transformer.TransformerImpl;
import wx.xml.xalan.xml.dtm.DTM;
import wx.xml.xalan.xml.dtm.DTMIterator;
import wx.xml.xalan.xml.utils.QName;
import wx.xml.xalan.xml.utils.XMLString;
import wx.xml.xalan.xpath.XPathContext;
import wx.xml.xalan.xpath.axes.UnionPathIterator;
import wx.xml.xalan.xpath.functions.Function2Args;
import wx.xml.xalan.xpath.objects.XNodeSet;
import wx.xml.xalan.xpath.objects.XObject;

/**
 * Execute the Key() function.
 *
 * @xsl.usage advanced
 */
public class FuncKey extends Function2Args {
    static final long serialVersionUID = 9089293100115347340L;

    /**
     * Dummy value to be used in usedrefs hashtable
     */
    static private Boolean ISTRUE = new Boolean(true);

    /**
     * Execute the function.  The function must return
     * a valid object.
     *
     * @param xctxt The current execution context.
     * @return A valid XObject.
     * @throws javax.xml.transform.TransformerException
     */
    public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException {

        // TransformerImpl transformer = (TransformerImpl)xctxt;
        TransformerImpl transformer = (TransformerImpl) xctxt.getOwnerObject();
        XNodeSet        nodes       = null;
        int             context     = xctxt.getCurrentNode();
        DTM             dtm         = xctxt.getDTM(context);
        int             docContext  = dtm.getDocumentRoot(context);

        if (DTM.NULL == docContext) {

            // path.error(context, XPATHErrorResources.ER_CONTEXT_HAS_NO_OWNERDOC); //"context does not have an owner document!");
        }

        String     xkeyname        = getArg0().execute(xctxt).str();
        QName      keyname         = new QName(xkeyname, xctxt.getNamespaceContext());
        XObject    arg             = getArg1().execute(xctxt);
        boolean    argIsNodeSetDTM = (XObject.CLASS_NODESET == arg.getType());
        KeyManager kmgr            = transformer.getKeyManager();

        // Don't bother with nodeset logic if the thing is only one node.
        if (argIsNodeSetDTM) {
            XNodeSet ns = (XNodeSet) arg;
            ns.setShouldCacheNodes(true);
            int len = ns.getLength();
            if (len <= 1)
                argIsNodeSetDTM = false;
        }

        if (argIsNodeSetDTM) {
            Hashtable         usedrefs = null;
            DTMIterator       ni       = arg.iter();
            int               pos;
            UnionPathIterator upi      = new UnionPathIterator();
            upi.exprSetParent(this);

            while (DTM.NULL != (pos = ni.nextNode())) {
                dtm = xctxt.getDTM(pos);
                XMLString ref = dtm.getStringValue(pos);

                if (null == ref)
                    continue;

                if (null == usedrefs)
                    usedrefs = new Hashtable();

                if (usedrefs.get(ref) != null) {
                    continue;  // We already have 'em.
                } else {

                    // ISTRUE being used as a dummy value.
                    usedrefs.put(ref, ISTRUE);
                }

                XNodeSet nl =
                    kmgr.getNodeSetDTMByKey(xctxt, docContext, keyname, ref,
                        xctxt.getNamespaceContext());

                nl.setRoot(xctxt.getCurrentNode(), xctxt);

//        try
//        {
                upi.addIterator(nl);
//        }
//        catch(CloneNotSupportedException cnse)
//        {
//          // will never happen.
//        }
                //mnodeset.addNodesInDocOrder(nl, xctxt); needed??
            }

            int current = xctxt.getCurrentNode();
            upi.setRoot(current, xctxt);

            nodes = new XNodeSet(upi);
        } else {
            XMLString ref = arg.xstr();
            nodes = kmgr.getNodeSetDTMByKey(xctxt, docContext, keyname,
                ref,
                xctxt.getNamespaceContext());
            nodes.setRoot(xctxt.getCurrentNode(), xctxt);
        }

        return nodes;
    }
}
